import java.awt.*;
import java.awt.geom.*;
import java.util.Date;

/**
* Ein Beispiel fuer eine Bewegung, die aus der Konvexkombination zweier
* geometrischer Transformationen erzeugt wird. Eine Ellipse in der oberen
* linken Ecke des Fensters wird schrittweise in eine anders geformte Ellipse
* in der unteren rechten Ecke des Fensters transformiert.
*
* @author Frank Klawonn
* Letzte Aenderung 21.01.2005
*/
public class ConvexCombTransforms extends Frame
{

  //Konstruktor
  public ConvexCombTransforms()
  {
    //Ermoeglicht das Schliessen des Fensters
    addWindowListener(new MyFinishWindow());
  }


  public void paint(Graphics g)
  {

    Graphics2D g2d = (Graphics2D) g;

    //Verwendung von Antialiasing, um die Raender weniger ausgefranst
    //erscheinen zu lassen
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);


    int xul, yul, xwidth, yheight;

    xul = 20;     //x-Koordinate zur Definition eines umschreibenden Rechtecks
                  //fuer eine Ellipse
    yul = 40;     //y-Koordinate zur Definition des Rechtecks
    xwidth = 50;  //Breite des Rechtecks
    yheight = 20; //Hoehe des Rechtecks

    //Erzeugung einer Ellipse, aus dem Rechteck.
    //Aus dieser Ellipse werden die Ausgangs- und die Endellipse konstruiert.
    Ellipse2D.Double elli = new Ellipse2D.Double(xul,yul,xwidth,yheight);


    //Mittelpunkt der Ellipse/des Rechtecks
    double xmid = xul+xwidth/2.0;
    double ymid = yul+yheight/2.0;


    //Transformation, mit der die Ausgangsellipse erzeugt wird.
    AffineTransform initialTransform = new AffineTransform();
    initialTransform.setToRotation(Math.PI/6,xmid,ymid);

    //Die Matrix, die die Transformation fuer die Ausgangsellipse beschreibt.
    //Die Matrix wird zur Berechnung der Konvexkombinationen benoetigt.
    double[] initialMatrix = new double[6];
    initialTransform.getMatrix(initialMatrix);


    //Transformation, mit der die Endellipse erzeugt wird.
    AffineTransform finalTransform = new AffineTransform();
    finalTransform.setToTranslation(500,200);
    finalTransform.concatenate(scalingWRTXY(xmid,ymid,2,0.5));
    finalTransform.rotate(3*Math.PI/4,xmid,ymid);

    //Die Matrix, die die Transformation fuer die Endellipse beschreibt.
    //Die Matrix wird zur Berechnung der Konvexkombinationen benoetigt.
    double[] finalMatrix = new double[6];
    finalTransform.getMatrix(finalMatrix);


    //Anzahl der Zwischenschritte/Bilder fuer die Transformation der
    //Ausgangs- in die Endellipse
    int steps = 200;


    //Die Ausgangsellipse
    Shape initialElli = initialTransform.createTransformedShape(elli);

    //Die Endellipse
    Shape finalElli = finalTransform.createTransformedShape(elli);

    //Dieser Shape wird die Zwischenellipsen enthalten
    Shape s;


    //Diese Transform wird die Konvexkombinationen der Ausgangs- und der
    //Endtransformation enthalten.
    AffineTransform intermediateTransform;


    double stepsDouble = steps; //Anzahl der Schritte als Double-Wert um
                                //dauerndes Casting in der Schleife zu vermeiden
    for (int i=0; i<=steps; i++)
    {
      //Berechnung der i-ten Konvexkombination
      intermediateTransform = new AffineTransform(
                  convexCombination(initialMatrix,finalMatrix,i/stepsDouble));

      //Erzeugung der Zwischenellipse
      s = intermediateTransform.createTransformedShape(elli);

      //kurze Verzoegerung bis das naechste Bild gezeichnet wird
      sustain(50);

      //Loeschen des gesamten Fensters
      clearWindow(g2d);

      //Zeichne die Zwischenellipse
      g2d.fill(s);

      //Zeichne die Ausgangsellipse in Gruen
      g2d.setPaint(Color.green);
      g2d.fill(initialElli);

      //Zeichne die Endellipse in Rot
      g2d.setPaint(Color.red);
      g2d.fill(finalElli);

    }
  }


  /**
  * Berechnet die Konvexkombination zweier Vektoren/Punkte (Arrays)
  *
  * @param a       Anfangspunkt
  * @param b       Endpunkt (muss die gleiche Laenge wie a haben)
  * @param alpha   Anteil, den der Anfangspunkt am der Konvexkombination haben
  *                soll. Es muss 0<=alpha<=1 gelten.
  *
  * @return        Die Konvexkombination (1-alpha)*a + alpha*b der beiden
  *                Vektoren
  */
  public static double[] convexCombination(double[] a, double[] b, double alpha)
  {
    double[] result = new double[a.length];

    for (int i=0; i<result.length; i++)
    {
      result[i] = (1-alpha)*a[i] + alpha*b[i];
    }

    return(result);
  }



  /**
  * Eine Methode zum Loeschen (weiss malen) des Fensters.
  *
  * @param g2d      Graphics2D-Objekt, auf das gezeichnet werden soll.
  */
  public static void clearWindow(Graphics2D g)
  {
    g.setPaint(Color.white);
    g.fill(new Rectangle(0,0,600,300));
    g.setPaint(Color.black);
  }


  /**
  * Berechnet eine Skalierung mit den Skalierungsfaktoren xs und ys
  * bzgl. des Punktes/Zentrums (x,y)
  *
  * @param x       x-Koordinate des Zentrums
  * @param y       y-Koordinate des Zentrums
  * @param xs      Skalierungsfaktor in x-Richtung
  * @param ys      Skalierungsfaktor in y-Richtung
  *
  * @return        Die entsprechende Skalierung
  */
  public static AffineTransform scalingWRTXY(double x, double y,
                                             double xs, double ys)
  {
    AffineTransform at = new AffineTransform();

    at.translate(x,y);
    at.scale(xs,ys);
    at.translate(-x,-y);

    return(at);
  }


  /**
  * Eine Methode, die eine Verzoegerung von t Millisekunden bewirkt.
  * Diese Methode wird hier nur verwendet, um das Programm moeglichst
  * einfach zu Halten. Sie hat jedoch rechenzeitintemsives aktives
  * Warten zur Folge. In realen Anwendungen sollte man Verzoegerungen
  * durch die Verwendung von Threads realisieren.
  *
  * @param t      Wartezeit in Millisekunden.
  */
  public static void sustain(long t)
  {
    long finish = (new Date()).getTime() + t;
    while( (new Date()).getTime() < finish ){}
  }


  public static void main(String[] argv)
  {
    ConvexCombTransforms f = new ConvexCombTransforms();
    f.setTitle("Bewegung mittels Konvexkombination von Transformationen");
    f.setSize(600,300);
    f.setVisible(true);
  }

}

